package com.kexio.core.security.jwt;

import com.kexio.core.properties.CoreProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.SecretKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * JWT服务类
 *
 * @author Kexio Team
 * @since 1.0.0
 */
public class JwtService {

    private static final Logger log = LoggerFactory.getLogger(JwtService.class);

    private final CoreProperties coreProperties;
    private final SecretKey secretKey;

    public JwtService(CoreProperties coreProperties) {
        this.coreProperties = coreProperties;
        this.secretKey = Keys.hmacShaKeyFor(coreProperties.getSecurity().getJwt().getSecret().getBytes());
    }

    /**
     * 生成JWT令牌
     *
     * @param subject   主体(通常是用户ID或用户名)
     * @param claims    声明信息
     * @param expireIn  过期时间(秒)
     * @return JWT令牌
     */
    public String generateToken(String subject, Map<String, Object> claims, int expireIn) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + expireIn * 1000L);

        return Jwts.builder()
                .setSubject(subject)
                .setClaims(claims)
                .setIssuer(coreProperties.getSecurity().getJwt().getIssuer())
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(secretKey)
                .compact();
    }

    /**
     * 生成JWT令牌(使用默认过期时间)
     *
     * @param subject 主体
     * @param claims  声明信息
     * @return JWT令牌
     */
    public String generateToken(String subject, Map<String, Object> claims) {
        return generateToken(subject, claims, coreProperties.getSecurity().getJwt().getExpire());
    }

    /**
     * 生成JWT令牌(简单版本)
     *
     * @param subject 主体
     * @return JWT令牌
     */
    public String generateToken(String subject) {
        return generateToken(subject, new HashMap<>());
    }

    /**
     * 生成刷新令牌
     *
     * @param subject 主体
     * @return 刷新令牌
     */
    public String generateRefreshToken(String subject) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("type", "refresh");
        return generateToken(subject, claims, coreProperties.getSecurity().getJwt().getRefreshExpire());
    }

    /**
     * 解析JWT令牌
     *
     * @param token JWT令牌
     * @return Claims声明
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(secretKey)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (JwtException e) {
            log.warn("解析JWT令牌失败: {}", e.getMessage());
            throw new IllegalArgumentException("无效的JWT令牌", e);
        }
    }

    /**
     * 验证JWT令牌
     *
     * @param token JWT令牌
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            parseToken(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 从令牌中获取主体
     *
     * @param token JWT令牌
     * @return 主体
     */
    public String getSubject(String token) {
        return parseToken(token).getSubject();
    }

    /**
     * 从令牌中获取声明
     *
     * @param token JWT令牌
     * @param key   声明键
     * @return 声明值
     */
    public Object getClaim(String token, String key) {
        return parseToken(token).get(key);
    }

    /**
     * 从令牌中获取声明
     *
     * @param token JWT令牌
     * @param key   声明键
     * @param clazz 声明值类型
     * @return 声明值
     */
    public <T> T getClaim(String token, String key, Class<T> clazz) {
        return parseToken(token).get(key, clazz);
    }

    /**
     * 检查令牌是否过期
     *
     * @param token JWT令牌
     * @return 是否过期
     */
    public boolean isTokenExpired(String token) {
        try {
            Date expiration = parseToken(token).getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 获取令牌剩余有效时间(秒)
     *
     * @param token JWT令牌
     * @return 剩余有效时间(秒)
     */
    public long getRemainingSeconds(String token) {
        try {
            Date expiration = parseToken(token).getExpiration();
            long remaining = (expiration.getTime() - System.currentTimeMillis()) / 1000;
            return Math.max(0, remaining);
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 检查是否为刷新令牌
     *
     * @param token JWT令牌
     * @return 是否为刷新令牌
     */
    public boolean isRefreshToken(String token) {
        try {
            String type = getClaim(token, "type", String.class);
            return "refresh".equals(type);
        } catch (Exception e) {
            return false;
        }
    }
}
